﻿namespace WillSortSimulation;

public class Simulation
{
    private const int SchoolNum = 3;
    private const int StudentTotal = 100;   // 总人数
    private const int StudentWanted = 95;   // 总招录人数
    private const int WillPlanNum = 2;      // 每人志愿个数
    
    private School[] schooles;
    private WillPlan[] plans;

    private SchoolGenerator schoolGen = new();
    private WillPlanGenerator planGen = new();

    private List<WillPlan> PickPlans(List<WillPlan> allPlans, int schoolId, int planIndex)
    {
        List<WillPlan> res = new List<WillPlan>();
        for (int i = allPlans.Count-1; i >= 0; i--)
        {
            if (allPlans[i].SchoolIDArray[planIndex] == schoolId)
            {
                res.Add(allPlans[i]);
                allPlans.RemoveAt(i);
            }
        }
        return res;
    }

    private void AdmissionPlan(int schoolID, List<WillPlan> plans, List<WillPlan> wanted, List<WillPlan> left)
    {
        int wantNum = schooles[schoolID].GetNeedStudent();

        if (wantNum <= 0)
        {
            left.AddRange(plans);
            return;
        }
        
        if (wantNum < plans.Count)
        {
            left.AddRange(plans);
            for (int i = 0; i < wantNum; i++)
            {
                var rand = RandomManager.Get().Next(0, left.Count);
                var plan = left[rand];
                plan.schoolGot = schoolID;
                wanted.Add(plan);
                left.RemoveAt(rand);
                schooles[schoolID].GotPlansIds.Add(plan.planID);
            }
        }
        else
        {
            wanted.AddRange(plans);
            for (int i = 0; i < wanted.Count; i++)
            {
                wanted[i].schoolGot = schoolID;
                schooles[schoolID].GotPlansIds.Add(wanted[i].planID);
            }
        }
    }

    private void SummaryPlans(List<WillPlan> planList)
    {
        var scoreTotal = new int[(int)PlanType.Count];
        var planCount = new int[(int)PlanType.Count];
        var leftCount = new int[(int)PlanType.Count];
        
        var schoolSumm = new int[(int)PlanType.Count][];
        for (int i = (int)PlanType.Default+1; i < (int)PlanType.Count; i++)
        {
            schoolSumm[i] = new int[SchoolNum];
        }
        Console.WriteLine($"totalPlans: {planList.Count}");
        Console.WriteLine("");
        for (int i = 0; i < WillPlanNum; i++)
        {
            var schoolArr = new int[SchoolNum];
            for (int j = 0; j < planList.Count; j++)
            {
                var schoolId = planList[j].SchoolIDArray[i];
                schoolArr[schoolId]++;
            }
            Console.WriteLine($"plan index: {i}");
            for (int j = 0; j < SchoolNum; j++)
            {
                Console.WriteLine($"schoolId: {j}, plans: {schoolArr[j]}");
            }
        }
        Console.WriteLine("");

        for (int i = 0; i < planList.Count; i++)
        {
            var typeIndex = (int)planList[i].planType;
            if (planList[i].schoolGot < 0)
            {
                leftCount[typeIndex] += 1;
                continue;
            }

            var school = schooles[planList[i].schoolGot];

            scoreTotal[typeIndex] += school.SchoolValue;
            planCount[typeIndex] ++;
            schoolSumm[typeIndex][school.SchoolID]++;
        }

        for (int i = (int)PlanType.Default+1; i < (int)PlanType.Count; i++)
        {
            if (planCount[i] == 0)
            {
                continue;
            }
            var average = (int)MathF.Round(scoreTotal[i] / (float)planCount[i]);
            Console.WriteLine($"type: {Enum.GetName((PlanType)i)}, count: {planCount[i]}, left: {leftCount[i]}, average: {average}");
            
            for (int j = 0; j < schooles.Length; j++)
            {
                if (schoolSumm[i][j] > 0)
                {
                    Console.WriteLine($"schoolId: {j}, \t count: {schoolSumm[i][j]}");
                }
            }
        }
        
    }
    
    private List<WillPlan> AdmissionSimulate()
    {
        int i = 0;
        var leftPlansRound = new List<WillPlan>(plans);
        var nextLeftPlansRound = new List<WillPlan>();
        var totalPlans = new List<WillPlan>();
        
        while ( i < WillPlanNum && leftPlansRound.Count != 0)
        {
            for (int j = 0; j < SchoolNum; j++)
            {
                var sPlans = PickPlans(leftPlansRound, j, i);
                List<WillPlan> got = new(), left = new();
                AdmissionPlan(j, sPlans, got, left);
                totalPlans.AddRange(got);
                nextLeftPlansRound.AddRange(left);
            }
            (leftPlansRound, nextLeftPlansRound) = (nextLeftPlansRound, leftPlansRound);
            nextLeftPlansRound.Clear();
            i++;
        }
        totalPlans.AddRange(leftPlansRound);
        return totalPlans;
    }
    
    public void Main()
    {
        schooles = schoolGen.GenerateSchoolBySort(SchoolNum, StudentWanted);
        var randomNum = (int)(StudentTotal * 0.2);
        plans = new WillPlan[StudentTotal];
        var plan1 = planGen.GeneratePlansRandom(randomNum, WillPlanNum, schooles);
        var plan2 = planGen.GeneratePlansSorted(StudentTotal-randomNum, WillPlanNum, schooles);

        Array.Copy(plan1, plans, plan1.Length);
        Array.Copy(plan2, 0, plans, plan1.Length, plan2.Length);
        
        Console.WriteLine("------------Schools----------");
        for (int i = 0; i < schooles.Length; i++)
        {
            schooles[i].Dump();
        }
        
        // Console.WriteLine("------------Wills----------");
        // for (int i = 0; i < plans.Length; i++)
        // {
        //     plans[i].Dump();
        // }
        //
        var totalPlans = AdmissionSimulate();
        
        // Console.WriteLine("------------Wills----------");
        // for (int i = 0; i < plans.Length; i++)
        // {
        //     plans[i].Dump();
        // }

        SummaryPlans(totalPlans);
    }
}